package zhoug.common.clip;

import android.content.Context;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.graphics.PorterDuff;
import android.graphics.PorterDuffXfermode;
import android.graphics.Rect;
import android.graphics.RectF;
import android.util.AttributeSet;
import android.view.GestureDetector;
import android.view.MotionEvent;
import android.view.View;

import zhoug.logging.Logger;

import androidx.annotation.Nullable;

/**
 * 拍照四周遮罩中间框选取图像
 * 可以移动,缩放
 *
 * @Author HK-LJJ
 * @Date 2020/3/25
 * @Description
 */
public class ClipFrameView extends View {
    private static final String TAG = ">>>ClipFrameView";
    //橡皮擦,擦掉矩形中间的内容
    private Paint mClearPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
    //中间矩形的画笔
    private Paint mPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
    //中间抓取图像的矩形
    protected RectF mFrameRect;
    private static final int DEF_BOUNDS = 30;
    private Rect mLimitBounds;
    //四周角的长度
    int cornerLength = 50;
    //四周角宽度
    int cornerLineWidth = 9;
    //遮罩颜色
    private int maskColor = Color.argb(150, 0, 0, 0);
    private int rectColor = Color.WHITE;
    //是否可以缩放
    private boolean scaleEnabled = true;
    //是否可以移动
    private boolean translateEnabled = true;
    /**
     * 触摸点所处的位置
     */
    private static final int CORNER_LEFT_TOP = 1;//左上角
    private static final int CORNER_RIGHT_TOP = 2;
    private static final int CORNER_RIGHT_BOTTOM = 3;
    private static final int CORNER_LEFT_BOTTOM = 4;
    private static final int TRANS_LINE_POINT = 5;//四个中心线(触发拖动)

    //触摸点在哪个角落
    private int touchPoint = -1;
    //触摸点的矩形的边长偏移量
    private static final int TOUCH_RECT_SIZE = 50;
    //触摸点的矩形:以触摸点为中心,TOUCH_RECT_SIZE为边长的正方形
    private RectF touchRect = new RectF();
    protected boolean isDrawFrameRect = true;
    protected boolean isDrawCornerLine = true;
    /**
     * 是否绘制9宫格
     */
    private boolean isDrawGrid = true;
    private boolean mIsLayoutFinish = false;

    //手势事件
    private GestureDetector gestureDetector;

    private GestureDetector.SimpleOnGestureListener onGestureListener = new GestureDetector.SimpleOnGestureListener() {
        @Override
        public boolean onScroll(MotionEvent e1, MotionEvent e2, float distanceX, float distanceY) {
            super.onScroll(e1, e2, distanceX, distanceY);
            translate(distanceX, distanceY);
            return true;
        }
    };

    public ClipFrameView(Context context) {
        this(context, null);
    }

    public ClipFrameView(Context context, @Nullable AttributeSet attrs) {
        this(context, attrs, 0);

    }

    public ClipFrameView(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
        init(context);
    }


    private void init(Context context) {
        setLayerType(View.LAYER_TYPE_SOFTWARE, null);
        mPaint.setStyle(Paint.Style.STROKE);
        mPaint.setColor(rectColor);
        mPaint.setStrokeWidth(1);

        mClearPaint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.CLEAR));
        //手势监听
        gestureDetector = new GestureDetector(context, onGestureListener);
    }

    @Override
    protected void onDraw(Canvas canvas) {
        super.onDraw(canvas);
        //画遮罩
        canvas.drawColor(maskColor);
        //擦掉中间
        canvas.drawRect(mFrameRect, mClearPaint);

        //画矩形
        if (isDrawFrameRect) {
            mPaint.setStrokeWidth(1);
            canvas.drawRect(mFrameRect, mPaint);
        }
        //画9宫格
        if (isDrawGrid) {
            mPaint.setStrokeWidth(1);
            float gridLineOffsetX = (mFrameRect.right - mFrameRect.left) / 3;
            float gridLineOffsetY = (mFrameRect.bottom - mFrameRect.top) / 3;
            //画垂直线
            float x = mFrameRect.left + gridLineOffsetX;
            canvas.drawLine(x, mFrameRect.top, x, mFrameRect.bottom, mPaint);
            x = mFrameRect.left + gridLineOffsetX * 2;
            canvas.drawLine(x, mFrameRect.top, x, mFrameRect.bottom, mPaint);
            //画水平线
            float y = mFrameRect.top + gridLineOffsetY;
            canvas.drawLine(mFrameRect.left, y, mFrameRect.right, y, mPaint);
            y = mFrameRect.top + gridLineOffsetY * 2;
            canvas.drawLine(mFrameRect.left, y, mFrameRect.right, y, mPaint);
        }

        //画4个角
        if (isDrawCornerLine) {
            drawCornerLine(canvas);
        }

    }

    /**
     * 画4个角
     *
     * @param canvas
     */
    private void drawCornerLine(Canvas canvas) {
        mPaint.setStrokeWidth(cornerLineWidth);
        //角线对齐矩形框外边,居中对齐设置为0
        float offset = cornerLineWidth >> 1;
        //角线对齐,否则四个角会缺一点
        float xLineOffset = cornerLineWidth - 1;
        // 左上
        canvas.drawLine(mFrameRect.left - xLineOffset, mFrameRect.top - offset, mFrameRect.left + cornerLength, mFrameRect.top - offset, mPaint);
        canvas.drawLine(mFrameRect.left - offset, mFrameRect.top - xLineOffset, mFrameRect.left - offset, mFrameRect.top + cornerLength, mPaint);
        //左下
        canvas.drawLine(mFrameRect.left - xLineOffset, mFrameRect.bottom + offset, mFrameRect.left + cornerLength, mFrameRect.bottom + offset, mPaint);
        canvas.drawLine(mFrameRect.left - offset, mFrameRect.bottom + xLineOffset, mFrameRect.left - offset, mFrameRect.bottom - cornerLength, mPaint);
        //右上
        canvas.drawLine(mFrameRect.right + xLineOffset, mFrameRect.top - offset, mFrameRect.right - cornerLength, mFrameRect.top - offset, mPaint);
        canvas.drawLine(mFrameRect.right + offset, mFrameRect.top - xLineOffset, mFrameRect.right + offset, mFrameRect.top + cornerLength, mPaint);
        //右下
        canvas.drawLine(mFrameRect.right + xLineOffset, mFrameRect.bottom + offset, mFrameRect.right - cornerLength, mFrameRect.bottom + offset, mPaint);
        canvas.drawLine(mFrameRect.right + offset, mFrameRect.bottom + xLineOffset, mFrameRect.right + offset, mFrameRect.bottom - cornerLength, mPaint);
        //水平中线
        float centerX = (mFrameRect.left + mFrameRect.right) / 2;
        canvas.drawLine(centerX - (cornerLength >> 1), mFrameRect.top - offset, centerX + (cornerLength >> 1), mFrameRect.top - offset, mPaint);
        canvas.drawLine(centerX - (cornerLength >> 1), mFrameRect.bottom + offset, centerX + (cornerLength >> 1), mFrameRect.bottom + offset, mPaint);
        //垂直中线
        float centerY = (mFrameRect.top + mFrameRect.bottom) / 2;
        canvas.drawLine(mFrameRect.left - offset, centerY - (cornerLength >> 1), mFrameRect.left - offset, centerY + (cornerLength >> 1), mPaint);
        canvas.drawLine(mFrameRect.right + offset, centerY - (cornerLength >> 1), mFrameRect.right + offset, centerY + (cornerLength >> 1), mPaint);

    }


    @Override
    protected void onSizeChanged(int w, int h, int oldw, int oldh) {
        super.onSizeChanged(w, h, oldw, oldh);
        mIsLayoutFinish = true;
        //最大边框
        if (mLimitBounds == null) {
            mLimitBounds = new Rect(DEF_BOUNDS, DEF_BOUNDS, DEF_BOUNDS, DEF_BOUNDS);
        }
        //
        if (mFrameRect == null) {
            int offsetX = (w - mLimitBounds.left - mLimitBounds.right) / 4;
            int offsetY = (h - mLimitBounds.bottom - mLimitBounds.top) / 4;
            mFrameRect = new RectF(mLimitBounds.left + offsetX, mLimitBounds.top + offsetY, w - offsetX, h - offsetY);
        }
    }


    //最小宽度
    private int getFrameRectMinWidth() {
        return cornerLength * 4;
    }

    //最小高度
    private int getFrameRectMinHeight() {
        return cornerLength * 4;
    }


    /**
     * 触摸事件监听
     *
     * @param event
     * @return
     */
    @Override
    public boolean onTouchEvent(MotionEvent event) {
        Logger.d(TAG, "onTouchEvent:");
        if (translateEnabled || scaleEnabled) {
            switch (event.getAction()) {
                case MotionEvent.ACTION_DOWN:
                   /* boolean b = computeTouchPoint(event);
                    Logger.d(TAG, "onTouchEvent:" + b);
                    if (translateEnabled && touchPoint == TRANS_LINE_POINT) {
                        gestureDetector.onTouchEvent(event);
                        return true;
                    }
                    return b;*/
                    boolean b = computeTouchPoint(event);
                    if (b && touchPoint == TRANS_LINE_POINT) {
                        gestureDetector.onTouchEvent(event);
                    }
                    return b;
                case MotionEvent.ACTION_MOVE:
                    if (translateEnabled && touchPoint == TRANS_LINE_POINT) {
                        gestureDetector.onTouchEvent(event);
                        return true;
                    } else if (scaleEnabled) {
                        return handleScale(event);
                    }
                    break;
            }
        } else {
            return super.onTouchEvent(event);
        }
        return super.onTouchEvent(event);
    }


    /**
     * 触摸的是四边
     *
     * @param event
     * @return
     */
    private boolean computeTouchPoint(MotionEvent event) {
        switch (event.getAction()) {
            case MotionEvent.ACTION_DOWN:
                //计算触摸的角
                float x = event.getX();
                float y = event.getY();
                touchRect.set(x - TOUCH_RECT_SIZE, y - TOUCH_RECT_SIZE, x + TOUCH_RECT_SIZE, y + TOUCH_RECT_SIZE);
                //四个角
                if (scaleEnabled) {
                    if (touchRect.contains(mFrameRect.left, mFrameRect.top)) {
                        touchPoint = CORNER_LEFT_TOP;
                        return true;
                    } else if (touchRect.contains(mFrameRect.left, mFrameRect.bottom)) {
                        touchPoint = CORNER_LEFT_BOTTOM;
                        return true;
                    } else if (touchRect.contains(mFrameRect.right, mFrameRect.top)) {
                        touchPoint = CORNER_RIGHT_TOP;
                        return true;
                    } else if (touchRect.contains(mFrameRect.right, mFrameRect.bottom)) {
                        touchPoint = CORNER_RIGHT_BOTTOM;
                        return true;
                    }
                }
                if (translateEnabled) {
                    //中线拖动
                    touchRect.set(x - cornerLength, y - cornerLength, x + cornerLength, y + cornerLength);
                    float centerX = (mFrameRect.left + mFrameRect.right) / 2;
                    float centerY = (mFrameRect.top + mFrameRect.bottom) / 2;

                    if (touchRect.contains(centerX, mFrameRect.top)
                            || touchRect.contains(centerX, mFrameRect.bottom)
                            || touchRect.contains(mFrameRect.left, centerY)
                            || touchRect.contains(mFrameRect.right, centerY)) {
                        touchPoint = TRANS_LINE_POINT;
                        return true;
                    }
                }
                /*if (touchRect.contains(mFrameRect.left, mFrameRect.top)) {
                    touchPoint = CORNER_LEFT_TOP;
                    return true;
                } else if (touchRect.contains(mFrameRect.left, mFrameRect.bottom)) {
                    touchPoint = CORNER_LEFT_BOTTOM;
                    return true;
                } else if (touchRect.contains(mFrameRect.right, mFrameRect.top)) {
                    touchPoint = CORNER_RIGHT_TOP;
                    return true;
                } else if (touchRect.contains(mFrameRect.right, mFrameRect.bottom)) {
                    touchPoint = CORNER_RIGHT_BOTTOM;
                    return true;
                } else {
                    //中线拖动
                    touchRect.set(x - cornerLength, y - cornerLength, x + cornerLength, y + cornerLength);
                    float centerX = (mFrameRect.left + mFrameRect.right) / 2;
                    float centerY = (mFrameRect.top + mFrameRect.bottom) / 2;

                    if (touchRect.contains(centerX, mFrameRect.top)
                            || touchRect.contains(centerX, mFrameRect.bottom)
                            || touchRect.contains(mFrameRect.left, centerY)
                            || touchRect.contains(mFrameRect.right, centerY)) {
                        touchPoint = TRANS_LINE_POINT;
                        return true;
                    }
                }*/
                break;
            case MotionEvent.ACTION_CANCEL:
            case MotionEvent.ACTION_UP:
                touchPoint = -1;
                break;
        }
        return false;
    }


    /**
     * 拉动角落放大缩小到触摸位置
     *
     * @param event
     * @return
     */
    private boolean handleScale(MotionEvent event) {
//        Logger.d(TAG, "scaleFrameRect:touchCorner=" + touchCorner);
        float x = event.getX();
        float y = event.getY();
        if (touchPoint == CORNER_LEFT_TOP) {
            scaleTo(x, y, mFrameRect.right, mFrameRect.bottom);
            return true;
        } else if (touchPoint == CORNER_LEFT_BOTTOM) {
            scaleTo(x, mFrameRect.top, mFrameRect.right, y);
            return true;
        } else if (touchPoint == CORNER_RIGHT_TOP) {
            scaleTo(mFrameRect.left, y, x, mFrameRect.bottom);
            return true;
        } else if (touchPoint == CORNER_RIGHT_BOTTOM) {
            scaleTo(mFrameRect.left, mFrameRect.top, x, y);
            return true;
        }
        return false;
    }

    /**
     * 计算frameRect 坐标的合法性并移动
     *
     * @param left
     * @param top
     * @param right
     * @param bottom
     */
    private void scaleTo(float left, float top, float right, float bottom) {
        int minWidth = getFrameRectMinWidth();
        int minHeight = getFrameRectMinHeight();
        if (right - left < minWidth) {
            left = mFrameRect.left;
            right = mFrameRect.right;
        }
        if (bottom - top < minHeight) {
            bottom = mFrameRect.bottom;
            top = mFrameRect.top;
        }
        int width = getWidth();
        int height = getHeight();
        left = Math.max(left, mLimitBounds.left);
        top = Math.max(top, mLimitBounds.top);
        right = Math.min(right, width - mLimitBounds.right);
        bottom = Math.min(bottom, height - mLimitBounds.bottom);
        mFrameRect.set(left, top, right, bottom);
        invalidate();
    }

    /**
     * 移动
     *
     * @param disX >0左移
     * @param disY >0 上移
     */
    private void translate(float disX, float disY) {
//        Logger.d(TAG, "translate:disX=" + disX + ",disY=" + disY);
        int width = getWidth();
        int height = getHeight();
        //右移
        if (disX < 0) {
            if (mFrameRect.right - disX > width - mLimitBounds.right) {
                disX = mFrameRect.right - (width - mLimitBounds.right);
            }
        } else {
            //左移
            if (mFrameRect.left - disX < mLimitBounds.left) {
                disX = mFrameRect.left - mLimitBounds.left;
            }
        }
        //上移
        if (disY > 0) {
            if (mFrameRect.top - disY < mLimitBounds.top) {
                disY = mFrameRect.top - mLimitBounds.top;
            }
        } else {
            //下移
            if (mFrameRect.bottom - disY > height - mLimitBounds.bottom) {
                disY = mFrameRect.bottom - (height - mLimitBounds.bottom);
            }
        }
        mFrameRect.offset(-disX, -disY);
        invalidate();
    }


    public void setScaleEnabled(boolean scaleEnabled) {
        this.scaleEnabled = scaleEnabled;
    }

    public boolean isScaleEnabled() {
        return scaleEnabled;
    }

    public void setTranslateEnabled(boolean translateEnabled) {
        this.translateEnabled = translateEnabled;
    }

    public boolean isTranslateEnabled() {
        return translateEnabled;
    }

    public RectF getFrameRect() {
        return mFrameRect;
    }


    public void setLimitBounds(int left, int top, int right, int bottom) {
        if (mLimitBounds == null) {
            mLimitBounds = new Rect(left, top, right, bottom);
        } else {
            mLimitBounds.left = left;
            mLimitBounds.top = top;
            mLimitBounds.right = right;
            mLimitBounds.bottom = bottom;
        }
        Logger.d(TAG, "setLimitBounds:" + mLimitBounds);
    }

    public void setFrameRect(int left, int top, int right, int bottom) {
       /* if (mFrameRect == null) {
            mFrameRect = new RectF();
        }
        int width = getWidth();
        int height = getHeight();
        if (mLimitBounds != null) {
            if (width > 0 && height > 0) {
                invalidate();
            }
        } else {
            mFrameRect.left = left;
            mFrameRect.top = top;
            mFrameRect.right = right;
            mFrameRect.bottom = bottom;
        }*/
    }

    public void fitLimitBounds() {
        int width = getWidth();
        int height = getHeight();
        if (width > 0 && height > 0) {
            if (mFrameRect == null) {
                mFrameRect = new RectF();
            }
            mFrameRect.left = mLimitBounds.left;
            mFrameRect.top = mLimitBounds.top;
            mFrameRect.right = width - mLimitBounds.right;
            mFrameRect.bottom = height - mLimitBounds.bottom;
            Logger.d(TAG, "mFrameRect:" + mFrameRect);
            invalidate();
        }
    }

    /**
     * @param ratio   宽高比
     * @param percent 小边的百分比
     */
    public void fitLimitBounds(float ratio, float percent) {
        int width = getWidth();
        int height = getHeight();
        if (width > 0 && height > 0) {
            if (mFrameRect == null) {
                mFrameRect = new RectF();
            }
            float left = mFrameRect.left;
            float top = mLimitBounds.top;
            float right = width - mLimitBounds.right;
            float bottom = height - mLimitBounds.bottom;
            float w = right - left;
            float h = bottom - top;
            if (w / h > ratio) {
                h = h * percent;
                w = h * ratio;
            } else {
                w = w * percent;
                h = w / ratio;
            }
            left = (width - w) / 2;
            top = (height - h) / 2;
            right = left + w;
            bottom = top + h;
            mFrameRect.left = left;
            mFrameRect.top = top;
            mFrameRect.right = right;
            mFrameRect.bottom = bottom;
            Logger.d(TAG, "mFrameRect:" + mFrameRect);
            invalidate();
        }
    }


}
